﻿using System.Collections.Generic;
using System.Linq;
using GeneticSharp.Domain.Chromosomes;
using GeneticSharp.Domain.Crossovers;

namespace GeneticSharpDemo
{
    public class PositionBaseSelfCrossover : OrderBasedCrossover
    {
        private readonly int StartIndex;

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="GeneticSharp.Domain.Crossovers.PositionBasedCrossover"/> class.
        /// </summary>
        public PositionBaseSelfCrossover(int startIndex)
        {
            StartIndex = startIndex;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Validates the parents.
        /// </summary>
        /// <param name="parents">The parents.</param>
        protected override void ValidateParents(IList<IChromosome> parents)
        {
            //if (parents.AnyHasRepeatedGene())
            //{
            //    throw new CrossoverException(this, "The Position-based Crossover (POS) can be only used with ordered chromosomes. The specified chromosome has repeated genes.");
            //}

            for (var index = 0; index < parents.Count; index++)
            {
                var c = parents[index];
                var list = c.GetGenes().Skip(StartIndex).ToList();
                var notRepeatedGenesLength = list.Distinct().Count();
                if (list.Count != notRepeatedGenesLength)
                {
                    throw new CrossoverException(this,
                        "The Position-based Crossover (POS) can be only used with ordered chromosomes. The specified chromosome has repeated genes.");
                }
            }
        }

        /// <summary>
        /// Creates the child.
        /// </summary>
        /// <param name="firstParent">First parent.</param>
        /// <param name="secondParent">Second parent.</param>
        /// <param name="swapIndexes">The swap indexes.</param>
        /// <returns>
        /// The child.
        /// </returns>
        protected override IChromosome CreateChild(IChromosome firstParent, IChromosome secondParent, int[] swapIndexes)
        {
            var secondParentSwapGenes = secondParent.GetGenes().Skip(StartIndex)
                 .Select((g, i) => new { Gene = g, Index = i })
                 .Where((g) => swapIndexes.Contains(g.Index - StartIndex))
                 .ToArray();

            using (var firstParentRemainingGenes = firstParent.GetGenes().Skip(StartIndex)
                .Except(secondParentSwapGenes.Select(element => element.Gene).ToArray()).GetEnumerator())
            {
                var child = firstParent.CreateNew();
                //下半片需要交叉
                var secondParentSwapGensIndex = 0;
                for (int i = StartIndex; i < firstParent.Length; i++)
                {
                    if (secondParentSwapGenes.Any(f => f.Index == i - StartIndex))
                    {
                        child.ReplaceGene(i, secondParentSwapGenes[secondParentSwapGensIndex++].Gene);
                    }
                    else
                    {
                        firstParentRemainingGenes.MoveNext();
                        child.ReplaceGene(i, firstParentRemainingGenes.Current);
                    }
                }

                var s = child.GetGenes().Skip(StartIndex).ToList();
                var cnt = s.Distinct().Count();
                var b = cnt == s.Count;
                return child;
            }
        }

        #endregion
    }
}